<!-- ocr识别批量上传 -->
<template>
  <el-card
    style="width: fit-content;height: fit-content;"
    shadow="never"
  >
    <div class="content">
      <div class="box">
        <!-- 图片盒子 -->
        <div
          v-for="item in msgsData"
          :key="item.key"
          class="card"
          @contextmenu.prevent="rightClick($event, item)"
          @click="toPreviewImg(item)"
        >
          <div
            class="title"
            :class="item.src&&'uploaded'"
          >
            {{ item.title }}
          </div>

          <template v-if="item.src">
            <img
              :src="item.src"
              :draggable="!props.disabled"
              @dragstart="handleDragStart($event, item)"
              @dragover="handleDragover($event, item)"
              @drop="handleDrop($event, item)"
            >
          </template>
          <template v-else>
            <el-upload
              :action="themeStore.upload"
              :headers="headers"
              :show-file-list="false"
              :on-success="(res) => handleSuccessOne(res, item)"
              :on-error="handleError"
              :before-upload="beforeAvatarUploadOne"
              :disabled="props.disabled"
            >
              <img
                src="@/assets/common/defaultImg.jpg"
                :draggable="false"
                @dragstart="handleDragStart($event, item)"
                @dragover="handleDragover($event, item)"
                @drop="handleDrop($event, item)"
              >
            </el-upload>
          </template>
        </div>
      </div>

      <!-- 右键菜单 -->
      <transition name="el-fade-in">
        <div
          v-show="offsetMenu"
          ref="menu"
          class="right-menu-opt"
          :style="offset"
        >
          <el-upload
            :action="themeStore.upload"
            :headers="headers"
            :show-file-list="false"
            :on-success="(res) => handleSuccessOne(res, handleRightData)"
            :on-error="handleError"
            :before-upload="beforeAvatarUploadOne"
          >
            <div class="menu-edit">
              <el-icon><UploadFilled /></el-icon>
              <span>上传</span>
            </div>
          </el-upload>

          <div
            v-if="handleRightData?.src"
            class="menu-edit"
            style="border-top: 1px solid #ccc;"
            @click="handleDelete"
          >
            <el-icon><Delete /></el-icon>
            <span>删除</span>
          </div>
        </div>
      </transition>

      <!-- 下方按钮 -->
      <div
        v-if="!props.disabled"
        class="batch-opt"
      >
        <el-button
          text
          type="primary"
          :icon="FullScreen"
          :loading="recognizeLoading"
          @click="recognize"
        >
          证件识别
        </el-button>
        <el-upload
          ref="uploadRef"
          multiple
          :action="themeStore.upload"
          :headers="headers"
          :show-file-list="false"
          :on-success="handleSuccess"
          :on-error="handleError"
          :before-upload="beforeAvatarUpload"
          :disabled="uploadLoading"
        >
          <el-button
            text
            :loading="uploadLoading"
            type="primary"
            :icon="Camera"
          >
            批量上传
          </el-button>
        </el-upload>
      </div>
    </div>

    <!-- 预览 -->
    <viewer
      v-if="!showAll"
      ref="viewer"
      class="viewer"
      :options="options"
      :images="previews"
      :rebuild="true"
      @inited="inited"
    >
      <template #default="scope">
        <img
          v-for="src in scope.images"
          :key="src"
          style="display: none;"
          :src="src"
        >

        <!-- 关闭预览 -->
        <div
          v-if="!showAll"
          class="btn-close"
          @click="showAll = true"
        >
          <el-icon
            color="#FFF"
            class="icon"
          >
            <Close />
          </el-icon>
        </div>
      </template>
    </viewer>
  </el-card>
</template>
<script>
import useUserStore from '@/store/user'
import useThemeStore from '@/store/theme'
import { UploadFilled, Delete, Camera, FullScreen, Close } from '@element-plus/icons-vue'
import { ocrRecognition } from '@/api/common'
export default {
  name: 'AppBatchUpload'
}
</script>
<script setup>
// 批量操作
const userStore = useUserStore()
const themeStore = useThemeStore()

const headers = { DT_TOKEN: userStore.token }

const props = defineProps({
  disabled: {
    type: Boolean,
    default: false
  },
  type: { // ocr识别类型 1:司机 2:车辆 3:混合 4:货主
    type: Number,
    default: null
  }
})

const emit = defineEmits(['recognizeSuccess'])

const msgsData = defineModel({
  type: Array,
  default: () => []
})

// 拖拽
const handleDragover = (e) => {
  e.preventDefault()
}

let dragData = null
const handleDragStart = (e, data) => {
  e.stopPropagation()

  dragData = data
}
const handleDrop = (e, data) => {
  e.preventDefault()

  const src = data.src
  data.src = dragData.src
  dragData.src = src
  // console.log(msgsData.value)
}

const uploadRef = ref(null)
// 拦截批量上传请求数量
const uploadLoading = ref(false)
let imgAmount = 0
const modelValueLength = computed(() => msgsData.value.filter(item => !item.src).length)
const beforeAvatarUpload = () => {
  resetOffsetMenu()
  if (imgAmount >= modelValueLength.value) return false
  imgAmount++
  uploadLoading.value = true
}

// 上传成功
let successImgAmount = 0
const handleSuccess = (res) => {
  for (let i = 0; i < msgsData.value.length; i++) {
    if (!msgsData.value[i].src) {
      msgsData.value[i].src = res.url
      break
    }
  }
  successImgAmount++
  if (successImgAmount === imgAmount) {
    resetUpload()
  }
}

// 上传失败
const handleError = () => {
  ElMessage.error('图片上传失败，请稍后重试')
  resetUpload()
}

// 重置上传loading
const resetUpload = () => {
  uploadLoading.value = false
  imgAmount = 0
  successImgAmount = 0
  uploadRef.value.clearFiles()
}

// 菜单显示
const offsetMenu = ref(false)
// 鼠标右键菜单位置
const offset = reactive({
  top: 0,
  left: 0
})
// 重置菜单状态
const resetOffsetMenu = () => {
  offsetMenu.value = false
  offset.top = 0
  offset.left = 0
}

// 右键菜单
const menu = ref(null)
// 右键选中数据
const handleRightData = ref({})
const rightClick = async (e, item) => {
  if (props.disabled) return
  handleRightData.value = item
  offsetMenu.value = true

  // 渲染菜单位置
  await nextTick()

  const { offsetWidth, offsetHeight } = menu.value

  const { offsetLeft, offsetTop } = e.currentTarget
  offset.left = offsetLeft + 90 - offsetWidth / 2 + 'px'
  offset.top = offsetTop + 90 - offsetHeight / 2 + 'px'
}

// 删除
const handleDelete = () => {
  resetOffsetMenu()
  handleRightData.value.src = ''
}

// 上传单张
const beforeAvatarUploadOne = () => {
  resetOffsetMenu()
  uploadLoading.value = true
}

const handleSuccessOne = (res, data) => {
  data.src = res.url
  resetUpload()
}

// 缩略相关
const previews = ref([])
const showAll = ref(true)
const toPreviewImg = (data) => {
  resetOffsetMenu()
  if (!data.src) return
  const srcs = msgsData.value.filter((item) => item.src).map(item => item.src)
  showAll.value = false
  previews.value = srcs
  options.value.initialViewIndex = srcs.indexOf(data.src)
}
// 预览相关
const options = ref({
  inline: true,
  navbar: false,
  title: false,
  keyboard: false,
  initialViewIndex: 0
})
const $viewer = ref('')
const inited = async (viewer) => {
  $viewer.value = viewer
}

// 证件识别
const recognizeLoading = ref(false)
const recognize = async () => {
  await validate()
  const obj = {
    type: props.type
  }
  recognizeLoading.value = true
  props.modelValue.forEach(item => {
    obj[item.key] = item.src
  })
  const data = await ocrRecognition(obj).finally(() => {
    recognizeLoading.value = false
  })
  emit('recognizeSuccess', data)
}

// 暴露必填校验方法
const validate = () => {
  return new Promise((resolve, reject) => {
    const data = msgsData.value.filter(item => item.required && !item.src).map(item => item.title)
    if (data.length) {
      const msgs = {
        message: '请上传：' + data.join('，'),
        type: 'warning'
      }
      ElMessage(msgs)
      reject(msgs)
    } else {
      resolve()
    }
  })
}

// 获取值
const getValue = () => {
  const obj = {}
  msgsData.value.forEach(item => {
    obj[item.key] = item.src
  })
  return obj
}

// 重置
const rest = () => {
  msgsData.value.forEach(item => {
    item.src = ''
  })
}

defineExpose({
  rest,
  getValue,
  validate
})
</script>

<style scoped lang='scss'>
.content{
  min-width: 572px;min-height: 572px;position: relative;
  .box{
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    // grid-template-rows: repeat(3, 1fr);
    grid-column-gap: 16px;
    grid-row-gap: 16px;
    position: relative;
    .card{
      width: 180px;
      height: 180px;
      box-shadow: 0 0 5px 0 var(--el-color-primary);
      border-radius: var(--el-card-border-radius);
      position: relative;
      overflow: hidden;

      .title{
        position: absolute;
        top: 0;
        left: 10px;
        padding: 6px;
        width: 16px;
        text-align: center;
        background: rgb(192 195 199 / 90%);
        color: #fff;
        &:after {
          content: '';
          position: absolute;
          top: 100%;
          left: 0;
          border: 14px solid rgb(192 195 199 / 90%);
          border-bottom-color: transparent;
        }
        &.uploaded{
          background: var(--el-color-primary);
          &:after {
            border: 14px solid var(--el-color-primary);
            border-bottom-color: transparent;
          }
        }
      }
      img{
        width: 180px;
        height:180px
      }
    }
  }
  .right-menu-opt{
    position: absolute;
    width: 100px;
    background-color: var(--el-bg-color-overlay);
    border-radius: 5px;
    margin: 0 auto;
    box-shadow: 0 0 5px 0 #b1b7bd;
    transition: all 0.2s ease;
    .menu-edit{
      display: flex;
      align-items: center;
      line-height: 35px;
      padding: 0 15px;
      &:hover{
        cursor: pointer;
      }
    }
  }
  .batch-opt{
    position: absolute;
    bottom: 6px;
    left: 0;
    right: 0;
    margin: 0 auto;
    width: 278px;
    height: 50px;
    background: var(--el-bg-color-overlay);
    border-radius: 10px;
    box-shadow: 0 0 8px 0 var(--el-color-primary);
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: 1fr;
    place-items: center;
  }
}

  .viewer{
    position: absolute;
    top: 0;
    left: 0;
    .btn-close{
      background-color: rgba(0, 0, 0, 50%);
      border-radius: 50%;
      cursor: pointer;
      height: 80px;
      overflow: hidden;
      position: absolute;
      z-index: 1;
      left: -40px;
      top: -40px;
      transition: background-color 0.15s;
      width: 80px;
      &:hover{
        background-color: rgba(0, 0, 0, 80%);
      }
      .icon{
        bottom: 15px;
        right: 15px;
        position: absolute;
      }
    }
  }
</style>
